﻿#region NeoPatterns
// 
// NeoPatterns framework is released under LGPLv3 license.
// See http://www.opensource.org/licenses/lgpl-3.0.html for more details.
// 
// Please visit http://neopatterns.wikidot.com for more information about NeoPatterns
// 
// Copyright (c) Pascal Craponne 2008
// 
#endregion

using System.Data.Caching;

namespace NeoPatterns.Patterns.Cache
{
    /// <summary>
    /// Velocity client implementation for the cache
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TValue"></typeparam>
    public class DistributedCache<TKey, TValue> : ICache<TKey, TValue>
    {
        /// <summary>
        /// The CacheFactory is used globally by all instances
        /// </summary>
        private static CacheFactory cacheFactory;
        private static CacheFactory CacheFactory
        {
            get
            {
                if (cacheFactory == null)
                    cacheFactory = new CacheFactory();
                return cacheFactory;
            }
        }

        private static readonly object cacheIDlock = new object();
        private static int cacheID;

        private string cacheName;
        /// <summary>
        /// Setting the cache name will load the cache handling instance
        /// </summary>
        public string CacheName
        {
            get { return cacheName; }
            set
            {
                cacheName = value;
                // don't know if thread safety is required here
                // [This topic is pre-release documentation and is subject to change in future releases. Blank topics are included as placeholders.] 
                // :)
                lock (CacheFactory)
                {
                    cache = CacheFactory.GetCache(cacheName);
                }
            }
        }

        private System.Data.Caching.Cache cache;
        private readonly int id;

        public DistributedCache()
        {
            lock (cacheIDlock)
                id = ++cacheID;
        }

        /// <summary>
        /// Converts a key to a string
        /// (this is probably risky, maybe should we implement some specific string converter)
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        protected virtual string GetKey(TKey key)
        {
            var literalKey = string.Format("{0}/{1}", id, key);
            return literalKey;
        }

        /// <summary>
        /// Tries to get the object from cache
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool TryGet(TKey key, out TValue value)
        {
            var objectValue = cache.Get(GetKey(key));
            // Velocity returns null if the object is missing from the cache
            // (we work with boxed objects anyway)
            if (objectValue == null)
            {
                // nothing here
                value = default(TValue);
                return false;
            }
            // cast/unbox and say OK
            value = (TValue)objectValue;
            return true;
        }

        /// <summary>
        /// Adds the object to the cache
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void Set(TKey key, TValue value)
        {
            cache.Put(GetKey(key), value);
        }
    }
}
